#region License Information
// Copyright (c) 2005 - 2009, Allan Spartacus Mangune (allan@owlpal.com)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, 
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this list 
//   of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice, this list of 
//   conditions and the following disclaimer in the documentation and/or other materials provided 
//   with the distribution.
//
// * Neither the name of Owlpal nor the names of its contributors may be used to endorse or 
//   promote products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
// FITNESS FOR AllowHtmlInComment PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 
// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
#endregion
using System;
using System.Security.Cryptography;

namespace Owlpal.Security
{
	public sealed class SaltedHash 
	{
		#region Members
		private readonly string salt;
		private readonly string hash;
		private const int saltLength = 20;
		#endregion

		#region Properties
        /// <summary>
        /// 
        /// </summary>
        public string Hash
        {
            get { return this.hash; }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public string Salt 
		{ 
			get { return this.salt; } 
		}
        #endregion

		#region Public Members
		/// <summary>
		/// 
		/// </summary>
		/// <param name="password"></param>
		/// <returns></returns>
        public SaltedHash Create(string password) 
		{
			string salt = CreateSalt();
			string hash = ComputeHash(salt, password);
			return new SaltedHash(salt, hash);
		}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="salt"></param>
        /// <param name="hash"></param>
        /// <returns></returns>
		public SaltedHash Create(string salt, string hash) 
		{
			return new SaltedHash(salt, hash);
		}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="password"></param>
        /// <param name="salt"></param>
        /// <returns></returns>
		public bool IsValid(string password, string salt) 
		{
            return this.hash.Equals(ComputeHash(salt, password));
		}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="password"></param>
        /// <returns></returns>
        public bool IsValid(string password)
        { 
            return this.hash.Equals(ComputeHash(this.salt, password));
        }
		#endregion

		#region Private Members
		/// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
		private string CreateSalt() 
		{
			return Convert.ToBase64String(CreateRandomBytes(saltLength));
		}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="len"></param>
        /// <returns></returns>
		private byte[] CreateRandomBytes(int len) 
		{
			byte[] randomBytes = new byte[len];
			new  RNGCryptoServiceProvider().GetBytes(randomBytes);
			return randomBytes;
		}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="salt"></param>
        /// <param name="password"></param>
        /// <returns></returns>
		private string ComputeHash(string salt, string password) 
		{
			return Convert.ToBase64String(new SHA256CryptoServiceProvider()
                .ComputeHash(System.Text.Encoding.UTF8.GetBytes(salt + password)));
		}
		#endregion

        #region Contructors
        /// <summary>
        /// 
        /// </summary>
        public SaltedHash()
        {

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="salt"></param>
        /// <param name="hash"></param>
        public SaltedHash(string salt, string hash)
        {
            this.salt = salt;
            this.hash = hash;
        }
        #endregion
    }
}

